/*
 * Project CHEN Rendering Engine : /utils.cpp
 * Created: 2020-12-16, Last modified:  2021-01-27
 * 
 * This file contains implementations of methods declared in ./utils.hpp
 * 
 * Copyright (C) 2021 Hineven (hineven@pku.edu.cn)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "scene.hpp"
namespace chen {
 
Scene::Scene(const Structure * s, const std::vector<const Light *> & lights): all(s) {
    // classify and initialize lights
    if(lights.size() == 0) {
        LOG(WARNING) << "No avaliable light found in scene. Is that okay?";
    }
    this->lights = lights;
    for(int i = 0; i<(int)lights.size(); i++) {
        lights[i]->strc = this->all;
        if(hasFlag(lights[i]->flags, FLIGHT_AREA))
            arealights.push_back((AreaLight*)lights[i]);
    }
    // pre-caculation to support random picking a light respect to the lights'
    // energy distribution.
    light_power_series = new Float[lights.size()];
    light_power_series[0] = lights[0]->power();
    for(int i = 1; i<(int)lights.size(); i++) {
        light_power_series[i] = 
            light_power_series[i-1]+lights[i]->power();
    }
    light_area_series = new Float[lights.size()];
    light_area_series[0] = lights[0]->area();
    for(int i = 1; i<(int)lights.size(); i++) {
        light_area_series[i] = 
            light_area_series[i-1]+lights[i]->area();
    }
}

const Light * Scene::pickLightRespectToArea(const Float & u, Float * pdf) const {
    Float total = light_area_series[lights.size()-1];
    Float cur = total*u;
    // guaranteed that lights with a power of 0 have no chance
    // to be chosen
    int index = std::lower_bound(
        light_area_series,
        light_area_series+lights.size(),
        cur
    ) - light_area_series;
    const Light * li = lights[min(index, (int)lights.size()-1)];
    if(!hasFlag(li->flags, FLIGHT_SAMPLE_LO)) return nullptr;
    *pdf = li->area()/total;
    return li;
}

const Light * Scene::pickLightRespectToPower (const Float & u, Float * pdf) const {
    Float total = light_power_series[lights.size()-1];
    Float cur = total*u;
    // guaranteed that lights with a power of 0 have no chance
    // to be chosen
    int index = std::lower_bound(
        light_power_series,
        light_power_series+lights.size(),
        cur
    ) - light_power_series;
    const Light * li = lights[min(index, (int)lights.size()-1)];
    if(!hasFlag(li->flags, FLIGHT_SAMPLE_LO)) return nullptr;
    *pdf = li->power()/total;
    return li;
}

}
